﻿using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using System.Collections;

namespace Tron
{
    public partial class LANMenuControl : UserControl
    {
        UdpClient client;
        Thread recThread;
        System.Web.Script.Serialization.JavaScriptSerializer serializedData;
        Message recMsg;
        private delegate void processOnMainThread();
        private processOnMainThread processOnMainThread_del;
        Player localPlayer;
        private MenuUserControl menu;
        private Label timeLabel;
                
        /**
         * Constructeur de LANMenuControl prenant le joueur local en paramètre (hôte de la patie LAN)
         * 
         * Player player: le joueur créant le LANMenuControl
         */
        public LANMenuControl()
        {
            InitializeComponent();

            this.Size = new Size(Constantes.Xmatrice * Constantes.tall, Constantes.Ymatrice * Constantes.tall + 50);
            this.lanMenuPanel.Size = new System.Drawing.Size(Constantes.Xmatrice * Constantes.tall, Constantes.Ymatrice * Constantes.tall + 50);
            this.Controls.Add(timeLabel);

            String playerName = Microsoft.VisualBasic.Interaction.InputBox("Entrez votre pseudo", "Pseudo", "", 0, 0); 
            this.localPlayer = new Player(playerName, null);

            availablesList.DisplayMember = "userName";
            // serializer
            serializedData = new System.Web.Script.Serialization.JavaScriptSerializer();
            // socket to communicate
            client = new UdpClient();
            client.EnableBroadcast = true;
            client.Client.Bind(new IPEndPoint(0, 1337));
            // thread where to listen
            recThread = new System.Threading.Thread(new System.Threading.ThreadStart(listen));
            recThread.Name = "recThread";
            recThread.IsBackground = true;
            recThread.Start();
            recMsg = new Message();
            processOnMainThread_del = new processOnMainThread(processDataOnMainThread);
        }

        /**
         * Ecoute le réseau pour recevoir les messages des autres instances du jeu.
         */
        public void listen()
        {
            System.Net.IPEndPoint ep = new System.Net.IPEndPoint(0, 1337);

            while (true)
            {
                byte[] recBytes = client.Receive(ref ep); // Bloquant, attend la lecture d'un objet sur le thread en Background

                Message msg = serializedData.Deserialize<Message>(new System.Text.UTF8Encoding().GetString(recBytes));
                
                recMsg = new Message(msg);
                System.Diagnostics.Debug.WriteLine("Received from " + ep + " " + recMsg);
                this.availablesList.Invoke(processOnMainThread_del);
            }
        }

        /**
         * Appelé par l'hote du serveur LAN
         */
        public void createServer()
        {
            availablesList.Items.Add(this.localPlayer.Name);
            // 1. Ici bouton "start" pour l'hote du serveur LAN.
            Button startButton = new Button();
            startButton.Text = "Start";
            startButton.Location = new Point(Constantes.Xmatrice * Constantes.tall - 80, Constantes.Ymatrice * Constantes.tall + 20);
            this.lanMenuPanel.Controls.Add(startButton);
            // 2. Layout de la ListBox
            this.availablesList.Size = new Size(Constantes.Xmatrice * Constantes.tall, Constantes.Ymatrice * Constantes.tall);
        }

        /**
         * Appelé par un joueur souhaitant joindre un serveur hôte.
         */
        public void joinServer()
        {
            // On envoie le joueur local
            ArrayList players = new ArrayList();
            players.Add(this.localPlayer.Name);
            Message msg = new Message(players, MessageDefinitions.playerJoined);
            sendMessage(msg);
        }

        /**
         * Envoie un message en broadcast sur le reseau via l'object UDPClient
         * 
         * Message msg: le message à envoyer
         * return void
         */
        private void sendMessage(Message msg)
        {
            string str = serializedData.Serialize(msg);
            int sentBytes = client.Client.SendTo((new System.Text.UTF8Encoding()).GetBytes(str), new System.Net.IPEndPoint(new System.Net.IPAddress(new byte[] { 255, 255, 255, 255 }), 1337));
            Console.WriteLine("sentBytes = " + sentBytes);
        }

        /**
         * Appelé lorsque le thread d'écoute sur réseau reçoit un message.
         * Met à jour la liste des joueurs connectés côté serveur, renvoie 
         * la liste des joueurs aux clients.
         */
        private void processDataOnMainThread()
        {
            System.Threading.Monitor.Enter(recMsg);
            Message receivedMsg = new Message(recMsg);
            System.Threading.Monitor.Exit(recMsg);

            switch (receivedMsg.type)
            {
                case MessageDefinitions.playerJoined:
                    if (!availablesList.Items.Contains(receivedMsg.players[0]))
                    {
                        // On l'ajoute à la liste des joueurs
                        availablesList.Items.Add(receivedMsg.players[0]);
                        // On construit puis envoie la nouvelle liste de joueurs aux clients
                        ArrayList players = new ArrayList();
                        for (int i = 0; i < availablesList.Items.Count; i++)
                        {
                            players.Add(availablesList.Items[i]);
                        }
                        Message listPlayers = new Message(players, MessageDefinitions.listPlayers);
                        sendMessage(listPlayers);
                    }
                    break;
                case MessageDefinitions.listPlayers:
                    // A la reception d'une liste de joueurs
                    availablesList.Items.Clear();
                    for (int j = 0; j < receivedMsg.players.Count; j++)
                    {
                            availablesList.Items.Add(receivedMsg.players[j]);
                    }
                    break;
                default:
                    break;
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void backToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Visible = !this.Visible;
            menu.Visible = !menu.Visible;
        }

        public MenuUserControl Menu
        {
            get
            {
                return menu;
            }
            set
            {
                menu = value;
            }
        }

        public Label TimeLabel
        {
            get
            {
                return timeLabel;
            }
            set
            {
                timeLabel = value;
            }
        }
    }
}
